package com.elloe.实现二叉树的Node节点.Node的Java实现;

import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

/**
 * @author ElloeStudy(Sifa Zhang)
 * @create 2022-04-09 13:04
 * To: 真常应物，真常得性，常清常静，常清静矣
 *
 * 自定义Node的节点
 */
public class Node {
    private int value;    // 节点的值
    private Node node;   // 当前节点
    private Node left;  // 此节点的左节点，类型为Node
    private Node right; // 此节点的右节点，数据类型为Node

    public Node() {
    }

    public Node(int value) {
        this.value = value;
        this.left = null;
        this.right = null;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public Node getNode() {
        return node;
    }

    public void setNode(Node node) {
        this.node = node;
    }

    public Node getLeft() {
        return left;
    }

    public void setLeft(Node left) {
        this.left = left;
    }

    public Node getRight() {
        return right;
    }

    public void setRight(Node right) {
        this.right = right;
    }

    @Override
    public String toString(){
        return this.value + " ";
    }

    // 构建二叉树
    public static void create(int[] datas, List<Node> list){
        // 将数组的数装换为节点Node
        for (int i = 0; i < datas.length; i++) {
            Node node = new Node(datas[i]);
            node.setNode(node);
            list.add(node);
        }

        // 节点关联树
        for (int index = 0; index < list.size()/2 - 1; index++) {
            //编号为n的节点他的左子节点编号为2*n 右子节点编号为2*n+1 但是因为list从0开始编号，所以还要+1
            list.get(index).setLeft(list.get(index * 2 + 1));
            list.get(index).setRight(list.get(index * 2 + 2));
        }

        // 单独处理最后一个节点，list.size()/2 -1 进行设置，避免单孩子情况
        int index = list.size()/2 - 1;
        list.get(index).setLeft(list.get(index * 2 + 1));
        if (list.size()%2 == 1){
            // 如果有奇数个节点，最后一个节点才有右节点
            list.get(index).setRight(list.get(index * 2 + 2));
        }
    }

    // 先序遍历（递归）
    // 传入需要遍历的节点
    public void preTraversal(Node node){
        // 当遇到叶节点，停止向下遍历
        if (node == null){
            return;
        }
        // 相当于点前节点的根节点的值
        System.out.print(node.getValue() + " ");
        // 先从底下依次遍历左节点
        preTraversal(node.getLeft());
        // 先从底下依次遍历右节点
        preTraversal(node.getRight());
    }


    // 中序遍历（递归）
    public void MidTraversal(Node node){
        // 判断当前节点是否为叶子节点，如果为叶子节点，停止遍历
        if (node == null){
            return;
        }
        // 获得左节点
        MidTraversal(node.getLeft());
        // 获得根节点
        System.out.print(node.getValue() + " ");
        // 获得右节点
        MidTraversal(node.getRight());
    }

    // 后序遍历（递归）
    public void afterTraversal(Node node){
        if (node == null){
            return;
        }
        afterTraversal(node.getLeft());
        afterTraversal(node.getRight());
        System.out.print(node.getValue() + " ");
    }


    // 非递归前序遍历
    public void  beforeTraversalByLoop(Node node){
        // 创建栈保存遍历的节点,但又没有遍历完全的节点（即这个节点还没有操作完，临时保存一下）
        Stack<Node> stack = new Stack<>();
        Node present = node;  // 当前的节点
        while (present != null || !stack.isEmpty()){
            // 当前的节点不为null 且 栈不为空

            while (present != null){
                // 当 当前的节点不为null时，读取present的值,
                // 并不断更新present为其左子节点（不断读取左节点的值）

                // 读取根节点
                System.out.print(present.getValue() + " ");
                stack.push(present); // 将present压入栈（此时这个节点还没有操作好，临时保存）
                present = present.getLeft(); // 读取当前节点的左节点
            }

            if (!stack.isEmpty()){
                // 当栈不为空时
                present = stack.pop(); // 将临时保存的数取出
                present = present.getRight();  // 操作临时保存的节点的右节点（此时左节点已经全部读取好了）
            }
        }
    }

    // 非递归中序遍历
    public void traversalMidByLoop(Node node) {
        // 创建栈保存遍历的节点,但又没有遍历完全的节点（即这个节点还没有操作完，临时保存一下）
        Stack<Node> stack = new Stack<>();
        Node present = node; // 当前操作的节点
        while (present != null || !stack.isEmpty()) {
            // 当前的节点不为null 且 栈不为空

            // 获取左节点
            while (present != null) {
                stack.push(present);// 将present压入栈（此时这个节点还没有操作好，临时保存）
                present = present.getLeft();// 读取当前节点的左节点
            }

            if (!stack.isEmpty()) {
                present = stack.pop();
                // 获取根节点
                System.out.print(present.getValue() + " ");
                present = present.getRight(); // 获取右节点
            }
        }
    }

    // 非递归后序遍历
    public void traversalAfterByLoop(Node node){
        // 存放还没有完成操作的节点，临时储存
        Stack<Node> stack = new Stack<>();
        Node present = node; // 当前的操作节点
        Node prev = node;  // 先前的根节点（一个标志flag）

        while (present != null || !stack.isEmpty()){
            // 当前的节点不为null 且 栈不为空

            while(present != null){
                // 如果当前的节点不为空

                stack.push(present); // 将当前这个节点临时存储
                present = present.getLeft(); // 遍历获取其左节点
            }

            if (!stack.isEmpty()){
                // 拿出栈顶的值，并没有进行删除
                Node temp = stack.peek().getRight(); // 获取栈顶节点的右节点

                // 节点没有右节点或者到达根节点【考虑到了最后一种情况】
                if (temp == null || temp == prev){
                    present = stack.pop();
                    // 获取根节点
                    System.out.print(present.getValue() + " ");

                    prev = present;   // 将当前的节点作为 根节点的标志（flag）
                    present = null;  // 将当前节点 设为空
                }else{
                    // 节点有右节点 或者 没有到达根节点
                    present = temp; // 将这个右节点设置为当前节点
                }
            }
        }
    }


    // 广度优先遍历
    public void bfs(Node root){
        if (root == null) {
            return ;
        }

        LinkedList<Node> queue = new LinkedList<>();
        queue.offer(root); // 将根节点存入队列

        //当队列里有值时，每次取出队首的node打印，打印之后判断node是否有子节点，
        // 若有，则将子节点加入队列
        while (queue.size() > 0){
            Node node = queue.peek(); // 查看队列的头部节点，不会删除节点
            queue.poll(); // 取出（移除）对首的节点并打印
            System.out.print(node.getValue() + " ");
            if (node.getLeft() != null){
                // 如果有左节点，则将其存入队列
                queue.offer(node.getLeft());
            }
            if (node.getRight() != null){
                // 如果有右节点，则将其存入对列
                queue.offer(node.getRight());
            }
        }
    }


    // 深度优先遍历
    public void dfs(Node root) {
        if (root == null){
            return;
        }

        Stack<Node> stack = new Stack<>();
        stack.push(root); // 将根节点压入栈里面

        while (!stack.isEmpty()){
            Node node = stack.pop(); // 弹出栈顶的节点
            System.out.print(node.getValue() + " ");

            // 深度优先遍历，先遍历左边在右边，所以先将右边压入再将左边压入
            if (node.getRight() != null){
                stack.push(node.getRight());
            }
            if (node.getLeft() != null){
                stack.push(node.getLeft());
            }
        }
    }
}
